/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.bytedance.scene.ui.view.ext;

import ohos.agp.colors.RgbColor;
import ohos.agp.components.AttrHelper;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.DirectionalLayout;
import ohos.agp.components.ScrollView;
import ohos.agp.components.StackLayout;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.render.Canvas;
import ohos.agp.utils.Color;
import ohos.app.Context;
import ohos.multimodalinput.event.TouchEvent;

/**
 * 侧边栏
 *
 * @since 2021-06-07
 */
public class SlideMenu extends StackLayout implements Component.TouchEventListener,
        ReboundHelper.ReboundOverListener, Component.ClickedListener {
    private static final int NUM200 = 200;
    private static final double NUM15 = 15;
    private static final double NUM2 = 2;

    SlideMenuStateListener slideMenuStateListener;


    private final int mDefaultTouchSlop = 30;

    private final int mMaxMasAlpha = 0xCC;

    private Context mContext;

    private Component mMenuRoot;

    private Component mMenuContent;

    private ReboundHelper mReboundHelper;

    private ShapeElement mask;

    private Component[] mIgnoreComponents;

    private boolean isShowMenu;

    private int mMenuWidth;

    private int mTouchSlop;

    private float mLastX;
    private float mLastY;
    private long mStartTime;

    private boolean isDragMenu;

    /**
     * SlideMenu
     *
     * @param context
     */
    public SlideMenu(Context context) {
        super(context);
        init(context);
    }

    /**
     * SlideMenu
     *
     * @param context
     * @param attrSet
     */
    public SlideMenu(Context context, AttrSet attrSet) {
        super(context, attrSet);
        init(context);
    }

    /**
     * SlideMenu
     *
     * @param context
     * @param attrSet
     * @param styleName
     */
    public SlideMenu(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
        init(context);
    }

    /**
     * init
     *
     * @param context
     */
    public void init(Context context) {
        mContext = context;
        isShowMenu = false;
        isDragMenu = false;
        mask = new ShapeElement();
        mReboundHelper = new ReboundHelper(this);
        setTouchSlopWithVP(mDefaultTouchSlop);
    }

    public void setContext(Context context) {
        mContext = context;
    }

    public void setTouchSlopWithVP(int touchSlop) {
        mTouchSlop = AttrHelper.vp2px(touchSlop, mContext);
    }

    public void setIgnoreComponents(Component... ignoreComponents) {
        this.mIgnoreComponents = ignoreComponents;
    }

    //是否测量出宽度
    boolean isMeasureWidth=false;
    /**
     * initMenu
     *
     * @param content
     */
    public void initMenu(Component content) {
        mMenuContent = content;
        mMenuContent.addDrawTask(new DrawTask() {
            @Override
            public void onDraw(Component component, Canvas canvas) {
                if(!isMeasureWidth){
                    mMenuWidth = component.getWidth();
                    mMenuContent.setTranslationX(-mMenuWidth);
                    isMeasureWidth=true;
                }
            }
        });

        StackLayout stackLayout = new StackLayout(mContext);
        LayoutConfig layoutConfig = new LayoutConfig(LayoutConfig.MATCH_PARENT, LayoutConfig.MATCH_PARENT);
        stackLayout.setLayoutConfig(layoutConfig);

        ScrollView svBg = new ScrollView(mContext);
        svBg.setLayoutConfig(layoutConfig);
        svBg.addComponent(new DirectionalLayout(mContext));
        svBg.setCanAcceptScrollListener((component, i, b) -> mMenuContent.getTranslationX() > -mMenuWidth);
        svBg.setBackground(mask);
        stackLayout.addComponent(svBg);
        stackLayout.addComponent(mMenuContent);

        mMenuRoot = stackLayout;
        mMenuRoot.setTouchEventListener(this);

        this.addComponent(mMenuRoot);
    }

    @Override
    public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
        float offsetX = touchEvent.getPointerScreenPosition(0).getX() - mLastX;

        switch (touchEvent.getAction()) {
            case TouchEvent.PRIMARY_POINT_UP:
            case TouchEvent.CANCEL:
                if (System.currentTimeMillis() - mStartTime <= NUM200 && offsetX < NUM15) {
                    if (isShowMenu && mLastX > mMenuWidth) {
                        mReboundHelper.reboundX(mMenuContent, -mMenuWidth);
                        setStopEnable(false);
                        return true;
                    }
                }
                cancelTouch();
                break;
            case TouchEvent.PRIMARY_POINT_DOWN:
                mStartTime = System.currentTimeMillis();
                mLastX = touchEvent.getPointerScreenPosition(0).getX();
                mLastY = touchEvent.getPointerScreenPosition(0).getY();
                if (!isShowMenu && mIgnoreComponents != null) {
                    for (Component ignoreComponent : mIgnoreComponents) {
                        if (isTouchIgnorComponent(ignoreComponent)) {
                            return false;
                        }
                    }
                }
                if (!isShowMenu && mLastX > mTouchSlop) {
                    setStopEnable(true);
                    return false;
                }
                isDragMenu = !isShowMenu;
                setStopEnable(false);
                return true;
            case TouchEvent.POINT_MOVE:
                if (isShowMenu && !isDragMenu) {
                    mLastX = touchEvent.getPointerScreenPosition(0).getX();
                    if (mLastX < mMenuWidth) {
                        isDragMenu = true;
                    }
                    break;
                }
                moveTouch(offsetX,touchEvent);
                break;
            default:
                break;
        }
        setStopEnable(false);
        return true;
    }

    private void moveTouch(float offsetX, TouchEvent touchEvent) {
        if (isDragMenu) {
            if (mMenuContent.getTranslationX() + offsetX > 0) {
                mMenuContent.setTranslationX(0);
            } else if (mMenuContent.getTranslationX() + offsetX < -mMenuWidth) {
                mMenuContent.setTranslationX(-mMenuWidth);
            } else {
                update(mMenuContent.getTranslationX() + offsetX, 0);
            }
            mLastX = touchEvent.getPointerScreenPosition(0).getX();
        }
    }

    private void cancelTouch() {

        if (mMenuContent.getTranslationX() > -mMenuWidth / NUM2) {
            mReboundHelper.duration(mMenuContent.getTranslationX() / -mMenuWidth).reboundX(mMenuContent, 0);
        } else {
            mReboundHelper.duration(1 - mMenuContent.getTranslationX() / -mMenuWidth)
                    .reboundX(mMenuContent, -mMenuWidth);
        }
    }

    private boolean isTouchIgnorComponent(Component ignoreComponent) {
        int[] position = ignoreComponent.getLocationOnScreen();
        if (mLastX >= position[0] && mLastX <= position[0] + ignoreComponent.getWidth()
                && mLastY >= position[1] && mLastY <= position[1] + ignoreComponent.getHeight()) {
            return true;
        }
        return false;
    }

    private void setStopEnable(boolean isEnable) {
        if (isEnable) {
            mMenuRoot.setClickable(false);
        } else {
            mMenuRoot.setClickedListener(this);
        }
    }

    /**
     * open
     */
    public void open() {
        if (!isShowMenu) {
            mReboundHelper.reboundX(mMenuContent, 0);
        }
    }

    public void open1(){
        mReboundHelper.reboundX(mMenuContent, 0);
    }

    public void close1(){
        mMenuContent.addDrawTask(new DrawTask() {
            @Override
            public void onDraw(Component component, Canvas canvas) {
                mMenuWidth = component.getWidth();
                mReboundHelper.stop();
                mReboundHelper.duration(0).reboundX(mMenuContent, -mMenuWidth);
            }
        });
    }

    /**
     * close
     */
    public void close() {
        mReboundHelper.stop();
        mReboundHelper.duration(0).reboundX(mMenuContent, -mMenuWidth);
    }

    @Override
    public void update(float offsetX, float offsetY) {
        mMenuContent.setTranslationX(offsetX);

        mask.setRgbColor(RgbColor.fromArgbInt(Color.argb((int)
                        (mMaxMasAlpha - mMenuContent.getTranslationX() * 1.0F / -mMenuWidth * mMaxMasAlpha),
                0, 0, 0)));
    }

    @Override
    public void over() {
        if (mMenuContent.getTranslationX() == -mMenuWidth) {
            isShowMenu = false;
            setStopEnable(true);
        } else {
            isShowMenu = true;
        }
        isDragMenu = false;

        if (slideMenuStateListener != null) {
            if (isShowMenu) {
                slideMenuStateListener.onSlideMenuOpened();
            } else {
                slideMenuStateListener.onSlideMenuClosed();
            }
        }
    }

    public boolean isShowMenu(){
        return isShowMenu;
    }

    @Override
    public void onClick(Component component) {
        // 屏蔽下层click事件
    }

    public void setSlideMenuStateListener(SlideMenuStateListener slideMenuStateListener) {
        this.slideMenuStateListener = slideMenuStateListener;
    }

    /**
     * SlideMenuStateListener
     *
     * @since 2021-06-07
     */
    public interface SlideMenuStateListener {
        /**
         * onSlideMenuOpened
         */
        void onSlideMenuOpened();

        /**
         * onSlideMenuClosed
         */
        void onSlideMenuClosed();
    }
}
